package GameEngine;

import java.util.ArrayList;

import AIEngine.AIPlayer;
/**
 *  The main class of PentagoPP by S.C.A.P.F.
 *  This class provides different game modes as you will see above
 *  It also handles the whole text game play and starts the gui
 */
public class GameLogic extends Thread{

	/**
	 * The main method of the whole game.<br>
	 * You can choose witch user interface you want to run:
	 * <ul>
	 *  <li>Graphical user Interface, based on "Processing", please type "gui"</li>
	 *  <li>Text mode for playing with console, please type "text"</li>
	 * </ul>
	 *
	 * After you have chosen the mode of the interface you will be able to choose the game play mode.
	 * The following modes are available for text interface:
	 * <ul>
	 *  <li>Player vs. Player</li>
	 *  <li>Player vs. AI</li>
	 *  <li>AI vs. AI</li>
	 *  </ul>
	 *
	 * The game will start in the mode you have chosen.
	 * @param args not able at this time
	 */

	private Board tBoard;
	private Time timeBlack;
	private Time timeWhite;
	private int leftTurns;
	private Move moveP1,moveP2;
	private GameSettings settings; 
	private GameMessage gameMessage;
	private ArrayList<Move> black_winningset;
	private ArrayList<Move> white_winningset;
	ArrayList<Double> blackMoveTimes;
	ArrayList<Double> whiteMoveTimes;
	private TestReport testReport;
	private int numTotTest;
	private int numActualTest;
	private boolean newGame = false;
	private boolean isFirstPlayerFirstMove = true;
	private boolean isSecondPlayerFirstMove = true;

	public GameLogic(){} 

	public GameLogic(GameMessage gameMessage,GameSettings gameSettings){
		tBoard = new Board();
		this.settings=gameSettings;
		this.gameMessage = gameMessage;
		black_winningset = new ArrayList<Move>();
		white_winningset = new ArrayList<Move>();
		blackMoveTimes = new ArrayList<Double>();
		whiteMoveTimes = new ArrayList<Double>();
	}

	public GameLogic(GameMessage gameMessage,GameSettings gameSettings,int numTotTest, int numActualTest){

		tBoard = new Board();
		this.settings=gameSettings;
		this.gameMessage = gameMessage;
		black_winningset = new ArrayList<Move>();
		white_winningset = new ArrayList<Move>();
		this.numTotTest=numTotTest;
		this.numActualTest=numActualTest;
		blackMoveTimes = new ArrayList<Double>();
		whiteMoveTimes = new ArrayList<Double>();
	}

	public void run(){ 

		Player white = null;
		Player black = null;

		int gameMode =settings.getMode(); 
		boolean color=settings.isFirstPlayer();
		int algType1=settings.getAlgType1(); 
		int algType2=settings.getAlgType2();
		int iaLevel1=settings.getIaLevel1();
		int iaLevel2=settings.getIaLevel2();
		tBoard.getStatus().value(settings.getStatus());

		if(numTotTest!=-1){
			testReport= new TestReport(algType1, algType2, iaLevel1, iaLevel2, numActualTest,numTotTest);
		}
		
		if(tBoard.getStatus().value() == Status.GAME_ON){
			// initialize chosen mode

			switch (gameMode) {

			case Status.GAME_PVP:
				black = new StdPlayer(true,gameMessage); 
				white = new StdPlayer(false,gameMessage);  
				play(black, white);

				if(numTotTest!=-1){
					testReport.writeMove(blackMoveTimes, whiteMoveTimes,tBoard.getStatus());
				}

				break;

			case Status.GAME_PVC:
				System.out.println("You have choosen Player vs. AI");              
				if (color) { // random mode for player colors
					black = new StdPlayer(true,gameMessage);
					white = new AIPlayer(false,algType1,iaLevel1);
					System.out.println("You are the black player!");
				}
				else{
					black = new AIPlayer(true,algType1,iaLevel1);
					white = new StdPlayer(false,gameMessage);
					System.out.println("You are the white player!");
				}
				play(black, white);

				break;

			case Status.GAME_CVC:
				black = new AIPlayer(true,algType1,iaLevel1);
				white = new AIPlayer(false,algType2,iaLevel2);
				play(black, white);
				if(numTotTest!=-1){	
					testReport.writeMove(blackMoveTimes, whiteMoveTimes,tBoard.getStatus());
					newGame = true;
				}
				break;
			}
		}
	}



	/**
	 * This method executes the game play of player 1 (black) and player 2 (white)
	 * A while loop will run until status changes from 0 (OK) to:
	 * <ul>
	 *  <li> 1 - black wins</li>
	 *  <li> 2 - white wins</li>
	 *  <li> 3 - draw</li>
	 *  <li>99 - error</li>
	 * </ul>
	 *
	 * @param p1 Black Player
	 * @param p2 white Player
	 */
	private void play(Player p1, Player p2) {
		
		timeBlack = new Time();
		timeWhite = new Time();

		// lets get us a empty Move
		moveP1 = null;
		moveP2 = null;

		// fin is false while its one players turn
		boolean fin = false;
		Message message = new Message();
		// loop while playing
		while (tBoard.getStatus().value() == Status.GAME_ON) {

			/* Get leftTurns */
			leftTurns = tBoard.getTurns();

			// request black move
			if (tBoard.getPlayer()) {
				while (!fin) {

					timeBlack.start(); // starts time counting

					try {
						System.out.println("Black (@) moves: ");
						fin = true;
						
						if(settings.getMode() == Status.GAME_CVC){
							timeBlack.start();
						}
						
						moveP1 = p1.request(moveP2, leftTurns);
					
						if(settings.getMode() == Status.GAME_CVC && !isFirstPlayerFirstMove){
							timeBlack.end();
							Double totalTime = new Double(timeBlack.getTotalTime());
							blackMoveTimes.add(totalTime);
						}
						if(settings.getMode() == Status.GAME_CVC && blackMoveTimes.isEmpty()){
							isFirstPlayerFirstMove = false;
						}
						
						if (moveP1 == null) {
							tBoard.getStatus().value(Status.WHITE_WINS);
							message.setStatus(Status.WHITE_WINS);
							gameMessage.sendMessageToGui(message);
						} else {
							tBoard.isValid(moveP1.column(),moveP1.row());
						
							if (tBoard.getStatus().value() == Status.ILLEGAL_MOVE) {
								message.setStatus(Status.ILLEGAL_MOVE);
								gameMessage.sendMessageToGui(message);
								throw new Exception("Invalid move!");
							}
							tBoard.setMarble(moveP1.column(), moveP1.row(), moveP1.black());
							tBoard.rotate((moveP1.element()), moveP1.clockwise());

						}
					} catch (Exception e) {
						fin = false;
						System.out.println(e.getMessage());
					}
					timeBlack.end(); // stops time counting
					System.out.println("Black time balance: " + timeBlack.getTotalTime() + "sec.");

					tBoard.checkWin(true, black_winningset); //checks if opposite may becomes winner
					tBoard.checkWin(false, white_winningset);

					if((settings.getMode() == Status.GAME_CVC 
							|| !settings.isFirstPlayer()) && tBoard.getStatus().value() != Status.ILLEGAL_MOVE){ //first player is IA
						message.setMove(moveP1);
					}

					message.setStatus(tBoard.getStatus().value());
					gameMessage.sendMessageToGui(message);
					/* Print game status (statistics) */
					System.out.println(tBoard);
					System.out.println("TEST NUMERO :"+this.numActualTest);
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}


				}

				if (tBoard.getStatus().value() == Status.BLACK_WINS || tBoard.getStatus().value() == Status.WHITE_WINS || tBoard.getStatus().value() == Status.DRAW){
					fin = true;
					System.out.println(black_winningset.toString());
					System.out.println(white_winningset.toString());

				}else{
					fin = false;
				}

			} else { // request white move
				while (!fin) {

					timeWhite.start(); //start time counting

					try {
						System.out.println("White (#) moves: ");
						fin = true;

						if(settings.getMode() == Status.GAME_CVC){
							timeWhite.start();
						}
						
						moveP2 = p2.request(moveP1, leftTurns);
						
						if(settings.getMode() == Status.GAME_CVC && !isSecondPlayerFirstMove){
							timeWhite.end();
							Double totalTime = new Double(timeWhite.getTotalTime());
							whiteMoveTimes.add(totalTime);
						}
						if(settings.getMode() == Status.GAME_CVC && whiteMoveTimes.isEmpty()){
							isSecondPlayerFirstMove = false;
						}
						
						
						if (moveP2 == null) {
							tBoard.getStatus().value(Status.BLACK_WINS);
							message.setStatus(Status.BLACK_WINS);
							gameMessage.sendMessageToGui(message);
						} else {
							tBoard.isValid(moveP2.column(),moveP2.row());
						
							if (tBoard.getStatus().value() == Status.ILLEGAL_MOVE) {
								message.setStatus(Status.ILLEGAL_MOVE);
								gameMessage.sendMessageToGui(message);
								throw new Exception("Invalid move!");
							}

							tBoard.setMarble(moveP2.column(), moveP2.row(), moveP2.black());
							tBoard.rotate((moveP2.element()), moveP2.clockwise());
						}
					} catch (Exception e) {
						fin = false;
						System.out.println(e.getMessage());
						System.out.println("TEST NUMERO :"+this.numActualTest);
					}

					timeWhite.end(); // stops time counting
					System.out.println("White time balance: " + timeWhite.getTotalTime() + "sec.");

					tBoard.checkWin(true, black_winningset); //checks if opposite may becomes winner
					tBoard.checkWin(false, white_winningset);					

					if((settings.getMode() == Status.GAME_CVC ||( settings.isFirstPlayer() && 
							settings.getMode() != Status.GAME_PVP))&& tBoard.getStatus().value() != Status.ILLEGAL_MOVE){ //first player is Human
						message.setMove(moveP2);
					}

					message.setStatus(tBoard.getStatus().value());
					gameMessage.sendMessageToGui(message);
					/* Print game status (statistics) */
					System.out.println(tBoard);
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}


				if (tBoard.getStatus().value() == Status.BLACK_WINS || tBoard.getStatus().value() == Status.WHITE_WINS || tBoard.getStatus().value() == Status.DRAW){
					fin = true;
					System.out.println(black_winningset.toString());
					System.out.println(white_winningset.toString());

				}else{
					fin = false;
				}
			}
			System.out.println("Stato del gioco: "+tBoard.getStatus().value());
		}

		switch (tBoard.getStatus().value()){
		case (Status.BLACK_WINS):
			message.setWinningSet(black_winningset);
			message.setStatus(Status.BLACK_WINS);
			gameMessage.sendMessageToGui(message);
			break;
		case (Status.WHITE_WINS):
			message.setWinningSet(white_winningset);
			message.setStatus(Status.WHITE_WINS);
			gameMessage.sendMessageToGui(message);
			break;
		case (Status.DRAW):
			message.setWinningSet(black_winningset);
			message.setStatus(Status.DRAW);
			gameMessage.sendMessageToGui(message);
			
			Message secondMessage = new Message();
			secondMessage.setWinningSet(white_winningset);
			secondMessage.setStatus(Status.DRAW);
			gameMessage.sendMessageToGui(secondMessage);
			break;
		
		}
	}

	public boolean isNewGame() {
		return newGame;
	}
}
